package top.lingkang.acgv.config.auth;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.TokenEnhancer;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JdbcTokenStore;
import top.lingkang.acgv.config.auth.handler.ExceptionTranslatorHandler;

@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private LoginUserDetailsService loginUserDetailsService;
    @Autowired
    private JdbcTemplate jdbcTemplate;
    @Autowired
    private ExceptionTranslatorHandler exceptionTranslatorHandler;

    @Bean
    public TokenStore tokenStore() {
        // 基于 redis 实现，令牌保存到redis，也可以存到数据库中
        return new JdbcTokenStore(jdbcTemplate.getDataSource());
    }

    @Bean
    @Primary
    public DefaultTokenServices defaultTokenServices() {
        DefaultTokenServices services = new DefaultTokenServices();
        services.setAccessTokenValiditySeconds(60 * 60 * 12);//设置token
        services.setRefreshTokenValiditySeconds(60 * 60 * 24 * 30);//设置刷新token的过期时间
        services.setTokenStore(tokenStore());
        return services;
    }

    @Bean
    public TokenEnhancer tokenEnhancer() {
        return new CustomTokenEnhancer();
    }


    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints
                .authenticationManager(authenticationManager)//支持password模式
                .userDetailsService(loginUserDetailsService) //在授权中再注入一次，用于令牌刷新
                // 自定义token生成规则
                .tokenEnhancer(tokenEnhancer())
        ;

        //自定义登录错误
        endpoints.exceptionTranslator(exceptionTranslatorHandler);
    }


    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        security
                // 允许客户端访问 /oauth/check_token 检查 token uri=/oauth/check_token?token=6662
                .checkTokenAccess("permitAll()")// isAuthenticated() 登录后才能访问
                // permitAll() 允许所有人 ；isAnonymous() 可以匿名访问
                // 允许所有资源服务器访问公钥端点（/oauth/token_key）
                .tokenKeyAccess("permitAll()")
                // 允许表单认证
                .allowFormAuthenticationForClients();
    }

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        // 配置客户端，一般是读取数据库的，因为可能会变动，这里我们写死放在内存中即可
        clients
                // 使用内存设置
                .inMemory()
                // client_id
                .withClient("client")
                // client_secret
                .secret(new BCryptPasswordEncoder().encode("secret"))
                // 授权类型
                .authorizedGrantTypes("password", "authorization_code", "refresh_token")
                //资源Id
                .resourceIds("backend-resources")
                // 授权范围
                .scopes("backend");
    }
}
